// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/builtins/builtins-utils-inl.h"
#include "src/builtins/builtins.h"
#include "src/counters.h"
#include "src/objects-inl.h"
#include "src/regexp/jsregexp.h"
#include "src/regexp/regexp-utils.h"
#include "src/string-builder-inl.h"

namespace v8 {
namespace internal {

    // -----------------------------------------------------------------------------
    // ES6 section 21.2 RegExp Objects

    BUILTIN(RegExpPrototypeToString)
    {
        HandleScope scope(isolate);
        CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.toString");

        if (*recv == isolate->regexp_function()->prototype()) {
            isolate->CountUsage(v8::Isolate::kRegExpPrototypeToString);
        }

        IncrementalStringBuilder builder(isolate);

        builder.AppendCharacter('/');
        {
            Handle<Object> source;
            ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
                isolate, source,
                JSReceiver::GetProperty(isolate, recv,
                    isolate->factory()->source_string()));
            Handle<String> source_str;
            ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, source_str,
                Object::ToString(isolate, source));
            builder.AppendString(source_str);
        }

        builder.AppendCharacter('/');
        {
            Handle<Object> flags;
            ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
                isolate, flags,
                JSReceiver::GetProperty(isolate, recv,
                    isolate->factory()->flags_string()));
            Handle<String> flags_str;
            ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, flags_str,
                Object::ToString(isolate, flags));
            builder.AppendString(flags_str);
        }

        RETURN_RESULT_OR_FAILURE(isolate, builder.Finish());
    }

// The properties $1..$9 are the first nine capturing substrings of the last
// successful match, or ''.  The function RegExpMakeCaptureGetter will be
// called with indices from 1 to 9.
#define DEFINE_CAPTURE_GETTER(i)                            \
    BUILTIN(RegExpCapture##i##Getter)                       \
    {                                                       \
        HandleScope scope(isolate);                         \
        return *RegExpUtils::GenericCaptureGetter(          \
            isolate, isolate->regexp_last_match_info(), i); \
    }
    DEFINE_CAPTURE_GETTER(1)
    DEFINE_CAPTURE_GETTER(2)
    DEFINE_CAPTURE_GETTER(3)
    DEFINE_CAPTURE_GETTER(4)
    DEFINE_CAPTURE_GETTER(5)
    DEFINE_CAPTURE_GETTER(6)
    DEFINE_CAPTURE_GETTER(7)
    DEFINE_CAPTURE_GETTER(8)
    DEFINE_CAPTURE_GETTER(9)
#undef DEFINE_CAPTURE_GETTER

    // The properties `input` and `$_` are aliases for each other.  When this
    // value is set, the value it is set to is coerced to a string.
    // Getter and setter for the input.

    BUILTIN(RegExpInputGetter)
    {
        HandleScope scope(isolate);
        Handle<Object> obj(isolate->regexp_last_match_info()->LastInput(), isolate);
        return obj->IsUndefined(isolate) ? ReadOnlyRoots(isolate).empty_string()
                                         : String::cast(*obj);
    }

    BUILTIN(RegExpInputSetter)
    {
        HandleScope scope(isolate);
        Handle<Object> value = args.atOrUndefined(isolate, 1);
        Handle<String> str;
        ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, str,
            Object::ToString(isolate, value));
        isolate->regexp_last_match_info()->SetLastInput(*str);
        return ReadOnlyRoots(isolate).undefined_value();
    }

    // Getters for the static properties lastMatch, lastParen, leftContext, and
    // rightContext of the RegExp constructor.  The properties are computed based
    // on the captures array of the last successful match and the subject string
    // of the last successful match.
    BUILTIN(RegExpLastMatchGetter)
    {
        HandleScope scope(isolate);
        return *RegExpUtils::GenericCaptureGetter(
            isolate, isolate->regexp_last_match_info(), 0);
    }

    BUILTIN(RegExpLastParenGetter)
    {
        HandleScope scope(isolate);
        Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info();
        const int length = match_info->NumberOfCaptureRegisters();
        if (length <= 2) {
            return ReadOnlyRoots(isolate).empty_string(); // No captures.
        }

        DCHECK_EQ(0, length % 2);
        const int last_capture = (length / 2) - 1;

        // We match the SpiderMonkey behavior: return the substring defined by the
        // last pair (after the first pair) of elements of the capture array even if
        // it is empty.
        return *RegExpUtils::GenericCaptureGetter(isolate, match_info, last_capture);
    }

    BUILTIN(RegExpLeftContextGetter)
    {
        HandleScope scope(isolate);
        Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info();
        const int start_index = match_info->Capture(0);
        Handle<String> last_subject(match_info->LastSubject(), isolate);
        return *isolate->factory()->NewSubString(last_subject, 0, start_index);
    }

    BUILTIN(RegExpRightContextGetter)
    {
        HandleScope scope(isolate);
        Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info();
        const int start_index = match_info->Capture(1);
        Handle<String> last_subject(match_info->LastSubject(), isolate);
        const int len = last_subject->length();
        return *isolate->factory()->NewSubString(last_subject, start_index, len);
    }

} // namespace internal
} // namespace v8
